/**
 * Created by PengWei on 2018/7/24.
 */

namespace mengdao {
    import TypeIdentifier = mengdao.TypeIdentifier;

    export class Binder<T> {

        /** 类型 */
        private readonly _typeIdentifier: TypeIdentifier<T>;

        /** 绑定类型 */
        private _binderType: BinderType;

        /** 绑定值 */
        private _binderValue: TypeIdentifier<T> | T;

        /** 实例 */
        private _instance: T;

        public constructor(typeIdentifier: TypeIdentifier<T>) {
            this._typeIdentifier = typeIdentifier;
        }

        /**
         * 将传入的Value作为一个常量的构造函数，
         * 因为是常量，所以即时创建实例
         * @param {{new(...args: any[]): T}} constructor
         * @param args
         */
        public toConstConstructor(constructor: { new(...args: any[]): T }, ...args): void {
            this._binderType = BinderType.AS_CONST;
            this._binderValue = new constructor(...args);
            this._instance = this._binderValue;
        }

        /**
         * 将传入的Value作为一个类定义，
         * 不即时创建，将会在第一次使用到时由getInstance创建
         * @param define
         */
        public toDefine(define: any): void {
            this._binderType = BinderType.AS_DEFINE;
            this._binderValue = define;
        }

        /**
         * 获得实例
         * @param args 构造函数参数
         * @returns {T} 实例
         */
        public getInstance(...args): T {
            if (this._instance)
                return this._instance;

            if (this._binderType == BinderType.AS_DEFINE) {
                this.toConstSelf(...args);
            }
            return this._instance;
        }

        public get rawInstance(): T {
            return this._instance;
        }

        /**
         * 将传入的Value当作是Key所对应的实例
         * @param {T} constValue
         */
        public toConstValue(constValue: T): void {
            this._binderType = BinderType.AS_CONST;
            this._binderValue = constValue;
            this._instance = this._binderValue;
        }

        /**
         * 将Key也当作Value,通过toConstConstructor创建实例
         * @see Binder.toConstConstructor
         * @param args
         */
        public toConstSelf(...args): void {
            if (typeof this._typeIdentifier !== "function") {
                throw new Error("Binder: Invalid to self value");
            }

            const self = this._typeIdentifier;
            return this.toConstConstructor(self, ...args);
        }
    }
}